package com.dj.mingkong.admin.boot.interceptor;


import com.dj.mingkong.admin.biz.constant.Constants;
import com.dj.mingkong.admin.biz.util.JwtUtil;
import com.dj.mingkong.common.annotation.HasRoles;
import com.dj.mingkong.common.constant.RedisConstants;
import com.dj.mingkong.common.enums.RoleEnum;
import com.dj.mingkong.common.exception.ExceptionCode;
import com.dj.mingkong.common.exception.ValidateException;
import com.dj.mingkong.common.infrastructure.base.dto.context.UserInfoContext;
import com.dj.mingkong.common.microservice.UserContext;
import jakarta.annotation.Resource;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * 拦截器
 *
 * @author zenglonghui
 */
@Component
@Slf4j
public class AppletTokenInterceptor implements HandlerInterceptor {
    @Resource
    JwtUtil jwtUtil;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) {
        log.info("开始拦截........");
        final String token = getToken(request);
        log.info("拦截器获取header Authorization:{}", token);
        if (StringUtils.isBlank(token) || Constants.UNDEFINED.equals(token)) {
            throw new ValidateException(ExceptionCode.UNAUTHORIZED.getCode(), ExceptionCode.UNAUTHORIZED.getMessage());
        }

        //解析token
        final UserInfoContext userInfoContext = jwtUtil.getUserInfoFromToken(token, UserInfoContext.class);
        log.info("解析token的信息userInfoContext:{}", Objects.isNull(userInfoContext) ? "null" : userInfoContext.toString());
        if (Objects.isNull(userInfoContext)) {
            throw new ValidateException(ExceptionCode.INVALID.getCode(), ExceptionCode.INVALID.getMessage());
        }

        //校验角色
        checkRole(handler, token);

        final String redisToken = redisTemplate.boundValueOps(RedisConstants.TOKEN + userInfoContext.getUsername()).get();
        if (StringUtils.isBlank(redisToken)) {
            throw new ValidateException(ExceptionCode.EXPIRED.getCode(), ExceptionCode.EXPIRED.getMessage());
        }

        if (!redisToken.equalsIgnoreCase(token)) {
            throw new ValidateException(ExceptionCode.REPETITION.getCode(), ExceptionCode.REPETITION.getMessage());
        }

        saveContext(userInfoContext);
        return true;
    }

    private void checkRole(Object handler, String token) {
        //方法注解
        final HasRoles methodAnnotation = ((HandlerMethod) handler).getMethodAnnotation(HasRoles.class);
        //类注解
        final HasRoles classAnnotation = ((HandlerMethod) handler).getBean().getClass().getAnnotation(HasRoles.class);
        final List<String> roles = jwtUtil.getRolesFromToken(token);
        log.info("解析token的信息roles:{}", roles.toString());
        if (CollectionUtils.isEmpty(roles)) {
            throw new ValidateException(ExceptionCode.NO_ACCESS.getCode(), ExceptionCode.NO_ACCESS.getMessage());
        }
        if (Objects.nonNull(methodAnnotation)) {
            validateCode(roles, methodAnnotation);
        } else if (Objects.nonNull(classAnnotation)) {
            validateCode(roles, classAnnotation);
        } else {
            if (!roles.contains(RoleEnum.ADMIN.getCode()) && !roles.contains(RoleEnum.AUDITOR.getCode())) {
                throw new ValidateException(ExceptionCode.NO_ACCESS.getCode(), ExceptionCode.NO_ACCESS.getMessage());
            }
        }
    }

    private void validateCode(List<String> roles, HasRoles annotation) {
        final List<RoleEnum> roleEnums = Arrays.stream(annotation.value()).toList();
        final List<String> list = roleEnums.stream().map(RoleEnum::getCode).toList();
        final Optional<String> optional = roles.stream().filter(list::contains).findFirst();
        if (optional.isEmpty()) {
            throw new ValidateException(ExceptionCode.NO_ACCESS.getCode(), ExceptionCode.NO_ACCESS.getMessage());
        }
    }

    private void saveContext(UserInfoContext userInfoContext) {
        UserContext.setUserObject(userInfoContext);
        UserContext.setUserId(userInfoContext.getId());
        UserContext.setUsername(userInfoContext.getUsername());
        final Long expire = redisTemplate.getExpire(RedisConstants.TOKEN + userInfoContext.getUsername(), TimeUnit.MINUTES);
        if (Objects.isNull(expire) || expire < Constants.FINE_MINUTES) {
            //重新设置过期时间
            redisTemplate.expire(RedisConstants.TOKEN + userInfoContext.getUsername(), RedisConstants.EXPIRE_TIME, TimeUnit.MINUTES);
        }
    }

    private String getToken(HttpServletRequest request) {
        final String headerToken = request.getHeader(Constants.AUTHORIZATION);
        if (StringUtils.isNotBlank(headerToken)) {
            return headerToken;
        } else if (StringUtils.isNotBlank(request.getQueryString())) {
            final String queryString = request.getQueryString();
            final String[] params = queryString.split("&");
            for (String param : params) {
                final String[] arr = param.split("=");
                if ("token".equalsIgnoreCase(arr[0])) {
                    return arr[1];
                }
            }
        }

        final Cookie[] cookies = request.getCookies();
        if (Objects.isNull(cookies)) {
            return null;
        }
        for (Cookie cookie : cookies) {
            if (Constants.AUTHORIZATION.equals(cookie.getName()) && StringUtils.isNotEmpty(cookie.getValue()) && cookie.getValue().length() > 2) {
                return cookie.getValue();
            }
        }
        return null;
    }

    @Override
    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
        log.info("调用完成后.........");
    }
}
